﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Net;
using MQTTnet;
using MQTTServer;
using System.Threading.Tasks;
using System.Windows.Forms;
using MQTTnet.Server;
using MQTTnet.Diagnostics;
using MQTTnet.Protocol;
using MQTTServer.Model;
using MQTTServer.Bll;

namespace MQTTServer
{
    public partial class Form1 : Form
    {
        private delegate void AddItemToListBoxDelegate(string str);
       
        public static IMqttServer mqttServer = null; //创建服务器对象
        private static List<string> connectedClientId = new List<string>();
        /// <summary>
        /// 公共订阅主题，各客户端上线后统一订阅此主题。用于各客户端之后保持相互通信。
        /// </summary>
        public  string PublicTopic = "PublicTopic";
        PayloadBll payloadBll = new PayloadBll();
        IPAddress ip ;
        int port = 4500;

        public Form1()
        {
            InitializeComponent();
        }
        // 建立一个MQTT服务.
        private void CreateMqttServer()
        {
            // Write all trace messages to the console window.
            MqttNetGlobalLogger.LogMessagePublished += MqttNetTrace_TraceMessagePublished;
            
            mqttServer = new MqttFactory().CreateMqttServer();
            mqttServer.ApplicationMessageReceived += MqttServer_ApplicationMessageReceived;//检查接收客户端发来的消息
            mqttServer.ClientSubscribedTopic += MqttServer_ClientSubscribedTopic;//客户端订阅事件
            mqttServer.ClientUnsubscribedTopic += MqttServer_ClientUnsubscribedTopic;//客户取消端订阅事件
            mqttServer.ClientConnected += MqttServer_ClientConnected;//检查客户端连接
            mqttServer.ClientDisconnected += MqttServer_ClientDisconnected;//检查客户端断开
            mqttServer.Started += MqttServer_Started;
            mqttServer.Stopped += MqttServer_Stopped;
        }

        private void MqttServer_ClientSubscribedTopic1(object sender, MqttClientSubscribedTopicEventArgs e)
        {
            throw new NotImplementedException();
        }

        private MqttServerOptionsBuilder InitMqttServerOptions()
        {
            ip = IPAddress.Parse(textBox1.Text.Trim());
            port =int.Parse( textBox2.Text.Trim());
            // 配置一个mqtt服务.
            MqttServerOptionsBuilder optionsBuilder = new MqttServerOptionsBuilder()
            //服务器IP
            .WithDefaultEndpointBoundIPAddress(ip)
             //连接记录数，默认 一般为2000
            .WithConnectionBacklog(2000)
            //服务器连接端口
            .WithDefaultEndpointPort(port)
            //连接验证器
           // .WithConnectionValidator(ValidatingMqttClients())
            //持续会话
            .WithPersistentSessions();
            return optionsBuilder;
        }

        private void button1_Click(object sender, EventArgs e)
        {
            if (mqttServer == null)
            {
                Task.Run(async () => {
                    await StartServer();
                });
            }
        }
        private   async Task StartServer()
        {
            CreateMqttServer();
           await mqttServer.StartAsync(InitMqttServerOptions().Build());
        }
        /// <summary>
        /// 客户端消息接收事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public void MqttServer_ApplicationMessageReceived(object sender, MqttApplicationMessageReceivedEventArgs e)
        {
            AddItemToListBox("全部消息接收事件:"+ "客户端:" + e.ClientId+ "--主题名Topic;" + e.ApplicationMessage.Topic+ "--服务质量等级QoS ;" + e.ApplicationMessage.QualityOfServiceLevel+ "--保留标志Retain ;" + e.ApplicationMessage.Retain+ "--有效载荷-消息体发送的数据 - Payload ; " + Encoding.UTF8.GetString(e.ApplicationMessage.Payload));                
        }

        // public EventHandler<MqttClientSubscribedTopicEventArgs> MqttServer_ClientSubscribedTopic { get; set; }
       /// <summary>
       /// 客户端订阅事件。订阅后服务器自动维护客户端集合和订阅关系。无需编码
       /// </summary>
       /// <param name="sender"></param>
       /// <param name="e"></param>
        public void MqttServer_ClientSubscribedTopic(object sender, MqttClientSubscribedTopicEventArgs e) {
            AddItemToListBox("客户端订阅事件:" +"客户端标识:"+ e.ClientId+"-" + "订阅主题:" + e.TopicFilter.Topic + "-" + "订阅级别:"+ e.TopicFilter.QualityOfServiceLevel+ "对象:"+ e.TopicFilter.ToString());
             
        }
        /// <summary>
        /// 客户端取消订阅事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public void  MqttServer_ClientUnsubscribedTopic(object sender, MqttClientUnsubscribedTopicEventArgs e) {
            AddItemToListBox("客户端取消订阅事件:客户端" + e.ClientId + "取消订阅--"+e.TopicFilter.ToString());
        }

        public void MqttServer_ClientConnected(object sender, MqttClientConnectedEventArgs e)
        {
            AddItemToListBox("客户端连接事件:客户端" + e.ClientId + "已连接");
            connectedClientId.Add(e.ClientId);

            ClientListModel clist = new ClientListModel();
            foreach (string str in connectedClientId)
            {
                ClientPayloadDataModel cdata = new ClientPayloadDataModel();
                cdata.ClientName = str;
                cdata.OnLineTime = DateTime.Now.ToString();
                clist.ClientList.Add(cdata);
            }
           
            //创建统一的消息体，准备发送数据
            PayloadDataBase pd = new PayloadDataBase();
            pd.PayloadType = 1;
            pd.DataContent = payloadBll.GetPayloadDataJSONStr_ByOBJ(clist);
            string jsonstr = payloadBll.GetPayloadDataJSONStr_ByOBJ(pd);
            //向所有客户端发送公共消息，推送当前所有客户名称
            Send_Topic_Payload_Data(PublicTopic, jsonstr);

        }

        public void MqttServer_ClientDisconnected(object sender, MqttClientDisconnectedEventArgs e)
        {

            AddItemToListBox("客户端断开事件:客户端" + e.ClientId + "已断开连接!");
            connectedClientId.Remove(e.ClientId);
        }
        /// <summary>
        /// 消息跟踪事件。MQTTnet 提供了一个静态类 MqttNetTrace 来对消息进行跟踪，该类可用于服务端和客户端。MqttNetTrace 的事件TraceMessagePublished 用于跟踪服务端和客户端应用的日志消息，比如启动、停止、心跳、消息订阅和发布等。事件参数MqttNetTraceMessagePublishedEventArgs 包含了线程ID ThreadId、来源 Source、日志级别 Level、日志消息 Message、异常信息 Exception 等。
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public void MqttNetTrace_TraceMessagePublished(object sender, MqttNetLogMessagePublishedEventArgs e)
        {
            string trace = "消息跟踪事件;" + e.TraceMessage.Source + "--" + e.TraceMessage.Message;
            if (e.TraceMessage.Exception != null)
            {
                trace += Environment.NewLine + e.TraceMessage.Exception.ToString();
            }
            AddItemToListBox(trace);
        }
        //关闭服务器
        private void button2_Click(object sender, EventArgs e)
        {
            Task.Run(async () => {
                await EndMqttServer();
            });
        }
        /// <summary>
        /// 停止MQTT服务器
        /// </summary>
        private async Task EndMqttServer()
        {
            if (mqttServer != null)
            {
              await  mqttServer.StopAsync();
            }
            //else
            //{
            //    AddItemToListBox("MQTT服务器已关闭！");
            //}
        }
        /// <summary>
        /// 给所有客户端发布发送指定的主题与数据-只有订阅此主题的客户端才会接收到此数据。此功能无需自己编码，MQTT帮我们实现了。
        /// </summary>
        /// <param name="Topic"></param>
        /// <param name="Payload_Data"></param>
        private async void Send_Topic_Payload_Data(string Topic, string Payload_Data)
        {
            //2.4.0版本的
            //var appMsg = new MqttApplicationMessage(topic, Encoding.UTF8.GetBytes(inputString), MqttQualityOfServiceLevel.AtMostOnce, false);
            //mqttClient.PublishAsync(appMsg);
            
            var message = new MqttApplicationMessageBuilder()
                .WithTopic(Topic)
                .WithPayload(Payload_Data)
                .WithAtMostOnceQoS()//设置QoS，至多1次。也可以设为别的。
                .WithRetainFlag(false)//发布保留标识，表示服务器要保留这次推送的信息，如果有新的订阅者出现，就把这消息推送给它，如果设有那么推送至当前订阅者后释放。
                .Build();
            await mqttServer.PublishAsync(message);
             
        }
        /// <summary>
        /// 给指定的客户订阅消息
        /// </summary>
        /// <param name="clientid"></param>
        /// <param name="topic"></param>
        private void SubscribeToClientId(string clientid, string topic)
        {
            List<TopicFilter> topicFilter = new List<TopicFilter>();
            topicFilter.Add(new TopicFilterBuilder()
                .WithTopic(topic)
                .WithAtMostOnceQoS()
                .Build());
            //给"client001"订阅了主题为topicFilter的payload
            mqttServer.SubscribeAsync(clientid, topicFilter);
             
            AddItemToListBox("订阅消息：" + clientid + ",Topic:" + topic);
        }
        /// <summary>
        /// MQTT连接验证
        /// </summary>
        /// <returns></returns>
        private Action<MqttConnectionValidatorContext> ValidatingMqttClients()
        {
            // Setup client validator.    
            var options = new MqttServerOptions();
            options.ConnectionValidator = c =>
            {
                Dictionary<string, string> c_u = new Dictionary<string, string>();
                c_u.Add("client001", "username001");
                c_u.Add("client002", "username002");
                Dictionary<string, string> u_psw = new Dictionary<string, string>();
                u_psw.Add("username001", "psw001");
                u_psw.Add("username002", "psw002");

                if (c_u.ContainsKey(c.ClientId) && c_u[c.ClientId] == c.Username)
                {
                    if (u_psw.ContainsKey(c.Username) && u_psw[c.Username] == c.Password)
                    {
                        c.ReturnCode = MqttConnectReturnCode.ConnectionAccepted;
                        AddItemToListBox("连接成功！ClientId：" + c.ClientId + "。用户名：" + c.Username + "。");
                    }
                    else
                    {
                        c.ReturnCode = MqttConnectReturnCode.ConnectionRefusedBadUsernameOrPassword;
                        AddItemToListBox("连接失败！用户名或密码错误！");
                    }
                }
                else
                {

                    c.ReturnCode = MqttConnectReturnCode.ConnectionRefusedIdentifierRejected;
                    AddItemToListBox("非法设备！" + c.ClientId + "--平台无法识别客户端标识(clientId),可能是设备未在平台进行激活");
                }
            };
            return options.ConnectionValidator;
        }
        private void AddItemToListBox(string str)
        {
            if (richTextBox1.InvokeRequired)
            {
                AddItemToListBoxDelegate d = AddItemToListBox;
                richTextBox1.Invoke(d,str);
            }
            else
            {
               string str2= richTextBox1.Text.Insert(0, str+"--"+DateTime.Now.ToString()+"\r\n");
               richTextBox1.Text = str2;
            }
        }


        public void MqttServer_Started(object sender, EventArgs e)
        {
            AddItemToListBox("服务器连接事件:MQTT服务器已启动！");
        }

        public void MqttServer_Stopped(object sender, EventArgs e)
        {
            mqttServer = null;
            AddItemToListBox("服务器关闭事件:MQTT服务器已关闭！");
        }
        /// <summary>
        /// 发布数据
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button3_Click(object sender, EventArgs e)
        {
            string Topic = textBox7.Text.Trim();
            string mesg = textBox8.Text.Trim();
            Send_Topic_Payload_Data(Topic, mesg);

        }
    }
}
